<?php

namespace Mlym\CodeGeneration;


use EasySwoole\Command\AbstractInterface\CommandHelpInterface;
use EasySwoole\Command\AbstractInterface\CommandInterface;
use EasySwoole\Command\Color;
use EasySwoole\Command\CommandManager;
use EasySwoole\Component\Di;
use EasySwoole\Component\Timer;
use EasySwoole\ORM\Db\Connection;
use EasySwoole\Utility\ArrayToTextTable;
use EasySwoole\Utility\Str;
use Exception;
use Mlym\CodeGeneration\InitGeneration\InitBaseGeneration;
use Swoole\Coroutine\Scheduler;
use Throwable;


class GenerationCommand implements CommandInterface
{
    /**
     * Command 名称
     * @return string
     */
    public function commandName(): string
    {
        return "generation";
    }

    /**
     * Command 描述
     * @return string
     */
    public function desc(): string
    {
        return 'wise-generator easyswoole Code auto generation tool';
    }

    /**
     * Command 帮助说明
     * @param CommandHelpInterface $commandHelp
     * @return CommandHelpInterface
     */
    public function help(CommandHelpInterface $commandHelp): CommandHelpInterface
    {
        $commandHelp->addAction('init', 'initialization');
        $commandHelp->addAction('all', 'specify build');

        $commandHelp->addActionOpt('--basePath', 'Action:init, Required:NO, specify base path');
        $commandHelp->addActionOpt('--moduleName', 'Action:all, Required:YES, specify module name');
        $commandHelp->addActionOpt('--modulePath', 'Action:all, Required:YES, specify module path');

        return $commandHelp;
    }

    /**
     * 执行命令
     * @return string
     */
    public function exec(): ?string
    {
        $action = CommandManager::getInstance()->getArg(0);
        $result = null;
        $run = new Scheduler();
        $run->add(function () use (&$result, $action) {
            switch ($action) {
                case 'init':
                    $result = $this->init();
                    break;
                case 'all':
                    $result = $this->all();
                    break;
                default:
                    $result = CommandManager::getInstance()->displayCommandHelp($this->commandName());
                    break;
            }
            Timer::getInstance()->clearAll();
        });
        $run->start();
        return $result . PHP_EOL;
    }


    /**
     * 基类初始化
     * @return ArrayToTextTable
     * @throws Exception
     */
    protected function init(): ArrayToTextTable
    {
        /**
         * 防止basePath未指定前缀路径
         */
        $basePath = CommandManager::getInstance()->getOpt('basePath') ?? '';
        if ($basePath) {
            Str::startsWith($basePath, "\\") || $basePath = "\\" . $basePath;
        }

        $initBaseGeneration = new InitBaseGeneration();
        $table = [];
        $table[0] = ['className' => 'Controller', 'filePath' => $initBaseGeneration->generationBaseController($basePath)];
        $table[1] = ['className' => 'Model', 'filePath' => $initBaseGeneration->generationBaseModel($basePath)];
        $table[2] = ['className' => 'Router', 'filePath' => $initBaseGeneration->generationRouter()];
        $table[3] = ['className' => 'Authorization', 'filePath' => $initBaseGeneration->generationAuthorization()];
        $exceptionGenerationList = $initBaseGeneration->generationException();
        foreach ($exceptionGenerationList as $class => $item) {
            $table[] = ['className' => $class, 'filePath' => $item];
        }

        return new ArrayToTextTable($table);
    }

    /**
     * 生成控制器、数据模型
     * @return ArrayToTextTable|string
     * @throws Throwable
     */
    protected function all()
    {
        /**
         * 获取参数
         */
        $moduleName = CommandManager::getInstance()->getOpt('moduleName');
        $modulePath = CommandManager::getInstance()->getOpt('modulePath');

        /**
         * 校验必要参数
         */
        if (empty($moduleName)) {
            return Color::error('--moduleName must be specified');
        }
        if (empty($modulePath)) {
            return Color::error('--modulePath must be specified');
        }

        $table = [];
        try {
            /**
             * 构建代码生成类，生成表对象
             */
            $codeGeneration = new CodeGeneration($moduleName);

            /**
             * 通过DI获取控制器、模型、单元测试、根路径等信息进行配置（在bootstrap注入）
             */
            $this->trySetDiGenerationPath($codeGeneration);

            if ($modulePath) {
                $tablePrefix = $codeGeneration->middlewareData->getModule()->getTablePrefix();
                $filePath = $codeGeneration->generationModel($modulePath, $tablePrefix);
                $table[] = ['className' => 'Model', "filePath" => $filePath];
                $filePath = $codeGeneration->generationController($modulePath, null, $tablePrefix);
                $table[] = ['className' => 'Controller', "filePath" => $filePath];
            }
        } catch (Exception $exception) {
            return Color::error($exception->getMessage());
        } catch (Throwable $e) {
            return Color::error($e->getMessage());
        }


        return new ArrayToTextTable($table);
    }

    /**
     * @return Connection
     * @throws Throwable
     */
    protected function getConnection(): ?Connection
    {
        $connection = Di::getInstance()->get('CodeGeneration.connection');
        if ($connection instanceof Connection) {
            return $connection;
        } elseif (is_array($connection)) {
            $mysqlConfig = new \EasySwoole\ORM\Db\Config($connection);
            $connection = new Connection($mysqlConfig);
            return $connection;
        } elseif ($connection instanceof \EasySwoole\ORM\Db\Config) {
            $connection = new Connection($connection);
            return $connection;
        }
        return null;
    }

    /**
     * 通过DI获取控制器、模型、单元测试、根路径等信息进行配置（在bootstrap注入）
     * @param CodeGeneration $codeGeneration
     * @throws Throwable
     */
    protected function trySetDiGenerationPath(CodeGeneration $codeGeneration)
    {
        $controllerBaseNameSpace = Di::getInstance()->get('CodeGeneration.controllerBaseNameSpace');
        $modelBaseNameSpace = Di::getInstance()->get('CodeGeneration.modelBaseNameSpace');
        $unitTestBaseNameSpace = Di::getInstance()->get('CodeGeneration.unitTestBaseNameSpace');
        $rootPath = Di::getInstance()->get('CodeGeneration.rootPath');
        if ($controllerBaseNameSpace) {
            $codeGeneration->setControllerBaseNameSpace($controllerBaseNameSpace);
        }
        if ($modelBaseNameSpace) {
            $codeGeneration->setModelBaseNameSpace($modelBaseNameSpace);
        }
        if ($unitTestBaseNameSpace) {
            $codeGeneration->setUnitTestBaseNameSpace($unitTestBaseNameSpace);
        }
        if ($rootPath) {
            $codeGeneration->setRootPath($rootPath);
        }
    }

}
